package codegen.client.mapper.task;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.Test;

import uk.co.q3c.deplan.client.domain.DomainObject;
import uk.co.q3c.deplan.client.domain.PropertyChangeListener;
import uk.co.q3c.deplan.client.domain.StructureChangeListener;
import uk.co.q3c.deplan.client.domain.task.BaseTask;
import uk.co.q3c.deplan.client.service.ServiceFactory;
import uk.co.q3c.deplan.client.ui.annotations.Calculated;
import uk.co.q3c.deplan.client.ui.view.Q3ViewBasePanel;
import uk.co.q3c.deplan.client.util.Q3StringUtil;
import uk.co.q3c.deplan.server.util.ReflectionFieldBuilder;
import uk.co.q3c.deplan.server.util.ReflectionFieldType;
import codegen.builders.CodegenClass;
import codegen.builders.CodegenConstructor;
import codegen.builders.CodegenInterface;
import codegen.builders.CodegenMethod;
import codegen.builders.CodegenMethodBase;
import codegen.builders.CodegenType;
import codegen.builders.Visibility;
import codegen.client.mapper.ReflectionFieldDescriptor;
import codegen.client.mapper.WidgetDef;

import com.google.gwt.user.client.ui.Label;
import com.googlecode.objectify.Key;

/**
 * Starts with a domainObject definition, and generates code for the creation of widgets for an associated view, with associated getters and
 * setters. There are default widget types defined in relation to data type, but these can be overruled. Two mapping methods are also
 * generated, to get data from the domain object to the widgets, and vice versa
 * 
 * @author David Sowerby 19 Mar 2010
 * 
 */
@Test
public class TaskMapper_Generator {

	String testKey;

	transient protected final Logger logger = Logger.getLogger(getClass().getName());
	Map<String, ReflectionFieldDescriptor> fieldMap;
	List<String> excludedFields = new ArrayList<String>();
	List<ReflectionFieldType> excludedFieldTypes = new ArrayList<ReflectionFieldType>();

	String fileHeader;

	Class<? extends DomainObject> dob = BaseTask.class;

	String projectRoot = "/home/dave/develop/workspace/DePlanGWT/src";
	String targetPackage = "uk.co.q3c.deplan.client.ui.view.auto";
	StringBuilder buf = new StringBuilder();

	CodegenClass cgViewClass;
	// used just to hold methods for invocation
	CodegenClass cgDomainObject;
	CodegenType cgPresenterClass;
	CodegenInterface cgViewInterface;
	// The name of the domainObject
	String objectName;
	int failures;

	public void generate() throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException {

		// prepare and name the class
		// at this point, only the class is modified until the interface is extracted from it
		cgViewClass = new CodegenClass(dob.getSimpleName() + "ViewBase");
		cgViewClass.packageName(targetPackage)
				.extend(Q3ViewBasePanel.class);

		objectName = Q3StringUtil.lowerCaseFirstChar(dob.getSimpleName());
		cgViewClass.addField(objectName, dob);

		// and the presenter
		cgPresenterClass = new CodegenClass(dob.getSimpleName() + "PresenterBase");
		cgPresenterClass.packageName(targetPackage);

		cgViewClass.addField("presenter", cgPresenterClass.fullName());
		cgPresenterClass.addField(objectName, dob)
				.getter(true)
				.setter(true);

		cgDomainObject = new CodegenClass("DomainObject");

		// Use reflection to get a list of fields from the domain object
		extractFieldsFromDomainObject(BaseTask.class);
		// Translated these into the fieldmap, using ReflectionFieldType to give a bit more information about the field and its use
		interpretFieldTypes(fieldMap);
		// annotations to indicate other features of the field - eg calculated
		specialFeatures(fieldMap);
		defineExcludedFields();
		defineExcludedFieldTypes();
		removeExclusions();
		assignWidgetNames();
		// set up getters and setters for view, and domain
		gettersAndSetters(fieldMap, dob);

		// set up label text in the field descriptor
		labelText(fieldMap, dob);

		// assign widgets to field records
		assignWidgets();
		// create label setters
		labelsetters();

		// set formats for those fields which use them when converting to text
		formats();

		// create the widgets in
		fieldsInUI();

		// can't use this view because we want to set value of widget not the widget itself
		// cgViewClass.buildGettersAndSetters();

		constructor();
		constructMethod();
		styleMethod();
		layoutMethod();
		handlersMethod();
		viewToObject();
		objectToView();
		CodegenInterface cgInterface = cgViewClass.extractInterface(cgViewClass, dob.getSimpleName() + "View");
		cgPresenterClass.addField("view", cgInterface.fullName())
				.getter(true)
				.setter(true);
		cgViewClass.outputToFile(projectRoot);
		cgPresenterClass.outputToFile(projectRoot);
		cgInterface.outputToFile(projectRoot);
		Assert.assertEquals(failures, 0, "Check the log for errors");
	}

	private void labelsetters() {
		for (ReflectionFieldDescriptor rfd : fieldMap.values()) {
			// Label
			String labelSetterName = Q3StringUtil.methodNamefromfieldName("set", rfd.widgetName + "LabelText");
			CodegenMethod m2 = cgViewClass.addMethod(labelSetterName);
			m2.visibility(Visibility.PUBLIC);
			m2.interfaceMember(true);
			m2.addParameter(String.class, "labelText");
			if (rfd.selfLabelling()) {
				m2.addBodyLine(rfd.widgetName + ".setLabelText(labelText)");
			} else {
				m2.addBodyLine(rfd.labelName + ".setText(labelText)");
			}

			rfd.viewSetLabelTextMethod = m2;
		}
	}

	private void specialFeatures(Map<String, ReflectionFieldDescriptor> fieldMap) {
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {
			fr.isCalculated = (fr.field.isAnnotationPresent(Calculated.class));
		}
	}

	/**
	 * Specify formats where special
	 */
	private void formats() {

		fieldMap.get("assignedUnits").format = "%#0.0";
		fieldMap.get("duration").format = "0.0d";

	}

	private void objectToView() {
		CodegenMethod m = cgPresenterClass.addMethod("objectToView");
		logger.debug("Object to view");
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {
			logger.debug("Adding body line for " + fr.name);
			String source = fr.domainGetValueMethod.invocationCode(objectName, null);
			if (fr.widgetSetDataValueMethod == null) {
				logger.error("WidgetSetDataValueMethod is not set for " + fr.name);
			}
			m.addBodyLine(fr.widgetSetDataValueMethod.invocationCode("view", new String[] { source }));
		}

		CodegenMethod m1 = cgPresenterClass.addMethod("prepareLabels");
		cgPresenterClass.getDefaultConstructor()
				.addBodyLine(m1.invocationCode(null, null));

		for (ReflectionFieldDescriptor fr : fieldMap.values()) {
			String source = null;
			source = "\"" + fr.labelText + "\"";
			if (fr.viewSetLabelTextMethod == null) {
				logger.error("viewSetLabelTextMethod has not been set for " + fr.name);
			}
			m1.addBodyLine(fr.viewSetLabelTextMethod.invocationCode("view", new String[] { source }));
		}

	}

	// private String widgetInput(ReflectionFieldDescriptor fr) {
	// if (fr.widgetType == CheckBox.class && fr.clazz == boolean.class) {
	// return (fr.widgetName + ".setValue" + valueOutputNoConvert(fr));
	//
	// }
	//
	// if (fr.widgetType == TextBox.class && fr.clazz == int.class) {
	// return (fr.widgetName + ".setText(" + propertyValueToWidget(cgViewClass, fr) + ")");
	//
	// }
	// if (fr.widgetType == TextBox.class && fr.clazz == long.class) {
	// return (fr.widgetName + ".setText(" + propertyValueToWidget(cgViewClass, fr) + ")");
	// }
	// if (fr.widgetType == TextBox.class && fr.clazz == Long.class) {
	// return (fr.widgetName + ".setText(" + propertyValueToWidget(cgViewClass, fr) + ")");
	// }
	// if (fr.widgetType == TextBox.class && fr.clazz == double.class) {
	// return (fr.widgetName + ".setText(" + propertyValueToWidget(cgViewClass, fr) + ")");
	// }
	//
	// if (fr.widgetType == TextBox.class && fr.clazz == String.class) {
	// return (fr.widgetName + ".setText(" + valueOutputNoConvert(fr) + ")");
	//
	// }
	// if (fr.widgetType == DateBox.class && fr.clazz == Date.class) {
	// return (fr.widgetName + ".setValue" + valueOutputNoConvert(fr));
	// }
	// // if (fr.widgetType == ListBox.class && fr.clazz == List.class) {
	// // generateListHandler(fr);
	// // return "";
	// // }
	// String msg = "No widgetInput-valueOutput combination defined for " + fr.widgetType + "-" + fr.clazz;
	// logger.warn(msg);
	// return "//" + msg;
	// }

	/**
	 * Codes a disclosure panel and loader for a list box
	 */
	private void generateListHandler(ReflectionFieldDescriptor fr) {
		// imports.add(DisclosurePanel.class.getName());
		// imports.add((OpenHandler.class.getName()));
		// imports.add((OpenEvent.class.getName()));
		// String disclosureName = lowerCaseFirstChar(fr.name) + "Disclosure";
		// fieldsBuf.append(fieldDeclaration(DisclosurePanel.class, disclosureName));
		// layoutMethodBuf.append("\t" + disclosureName + ".add(" + fr.widgetName + ");\n");
		// String loaderName = "load" + fr.name + "List";
		// handlersMethodBuf.append("\t" + disclosureName + ".addOpenHandler(new OpenHandler<DisclosurePanel>() {\n");
		// handlersMethodBuf.append("\t@Override\n");
		// handlersMethodBuf.append("\t\tpublic void onOpen(OpenEvent<DisclosurePanel> event) {\n");
		// handlersMethodBuf.append("\t\t\t" + loaderName + "();\n");
		// handlersMethodBuf.append("\t}\n});");
		// generateListLoaderMethod(loaderName, fr);
	}

	private void generateListLoaderMethod(String loaderName, ReflectionFieldDescriptor fr) {
		// imports.add((List.class.getName()));
		// imports.add((NamedObject.class.getName()));
		// loadListMethodsBuf.append("protected void " + loaderName + "(){\n");
		// loadListMethodsBuf.append("for (NamedObject nob : " + objectName + "." + fr.getter + "()) {");
		// loadListMethodsBuf.append(fr.widgetName + ".addItem(nob.getName());\n}\n");
		// loadListMethodsBuf.append("}\n");

		// for (NamedObject nob : BaseTask.getSuccessors()) {
		// successorsWidget.addItem(nob.getName());
		// }
	}

	// BaseTask.setActualFinish(view.getActualFinishWidgetValue());
	private void viewToObject() {
		CodegenMethod m = cgPresenterClass.addMethod("viewToObject");
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {
			if (!fr.isCalculated) {
				String source = fr.viewGetWidgetDataValueMethod.invocationCode("view", null);
				m.addBodyLine(fr.domainSetValueMethod.invocationCode(objectName, new String[] { source }));
			}
		}

	}

	private void handlersMethod() {
		cgViewClass.addMethod("handlers")
				.visibility(Visibility.PROTECTED);

	}

	private void layoutMethod() {
		CodegenMethodBase m = cgViewClass.addMethod("layout")
				.visibility(Visibility.PROTECTED);

	}

	private void styleMethod() {
		cgViewClass.addMethod("styles")
				.visibility(Visibility.PROTECTED);
	}

	private void constructMethod() {
		CodegenMethod m = cgViewClass.addMethod("construct");
		// constructMethodBuf.append("protected void construct(){\n");
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {

			m.addBodyLine(fr.widgetDef.construct(fr.widgetName));
			if (fr.selfLabelling()) {
				m.addBodyLine(fr.widgetName + ".setLabelText(\"" + fr.labelText + "\")");
			} else {
				m.addBodyLine(constructALabel(fr.labelName, fr.labelText));
			}

		}

	}

	// private String constructAWidget(Class<? extends Widget> widgetType, String widgetName) {
	// return widgetName + " = new " + widgetType.getSimpleName() + "()";
	// }

	private String constructALabel(String labelName, String labelText) {
		String s = "\"" + labelText + "\"";
		return labelName + " = new Label(" + s + ")";
	}

	/**
	 * Codes the constructor with calls to standard methods such as construct(), handlers() etc
	 */
	private void constructor() {
		cgViewClass.addImport(cgPresenterClass.fullName());
		CodegenConstructor c = cgViewClass.getDefaultConstructor();
		c.addBodyLine("presenter = new " + cgPresenterClass.getSimpleName() + "()")
				.addBodyLine("presenter.setView(this)")
				.addBodyLine("construct()")
				.addBodyLine("styles()")
				.addBodyLine("layout()")
				.addBodyLine("handlers()")
				.addBodyLine("presenter.set" + dob.getSimpleName() + "(" + objectName + ")");

	}

	// private String fieldDeclaration(Class<?> clazz, String objectName) {
	// return ("\tprotected " + clazz.getSimpleName() + " " + objectName + ";\n");
	//
	// }

	// private void classDef() {
	// buf.append("public class " + targetClassName() + " extends Q3ViewBasePanel{\n\n");
	// }

	private String targetClassName() {
		return dob.getSimpleName() + "ViewBase";
	}

	private void removeExclusions() {
		for (ReflectionFieldType rft : excludedFieldTypes) {
			for (Entry<String, ReflectionFieldDescriptor> entry : fieldMap.entrySet()) {
				logger.debug(entry.getValue().type);
				if (entry.getValue().type.equals(rft)) {
					excludedFields.add(entry.getKey());
				}
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Excluded fields");
			for (String exclude : excludedFields) {
				logger.debug(exclude);
			}

		}
		for (String exclude : excludedFields) {
			fieldMap.remove(exclude);
		}

	}

	protected void defineExcludedFields() {
		excludedFields.add("initialised");
		excludedFields.add("connection");
	}

	protected void defineExcludedFieldTypes() {
		// excludedFieldTypes.add(ReflectionFieldType.REFERENCEKEYLIST);
		// excludedFieldTypes.add(ReflectionFieldType.REFERENCEKEY);
		excludedFieldTypes.add(ReflectionFieldType.CONTROL);
	}

	private void extractFieldsFromDomainObject(Class<?> clazz) throws ClassNotFoundException, SecurityException, NoSuchMethodException {
		ReflectionFieldBuilder rfb = new ReflectionFieldBuilder();
		rfb.setExcludeTransient(false);
		ArrayList<String> propertyNames = new ArrayList<String>();
		Map<String, Field> baseFieldMap = rfb.buildOrderedFieldList(new BaseTask(null), propertyNames);
		createFieldMap(baseFieldMap);
		logger.info("completed build records");
	}

	private void interpretFieldTypes(Map<String, ReflectionFieldDescriptor> fieldMap) throws SecurityException, NoSuchMethodException,
			ClassNotFoundException {

		for (ReflectionFieldDescriptor rfd : fieldMap.values()) {
			interpretFieldType(rfd);
		}

	}

	private void interpretFieldType(ReflectionFieldDescriptor rfd) throws SecurityException, NoSuchMethodException, ClassNotFoundException {
		// remove the referred target as well - it won;t have its own widget
		if (rfd.clazz == Key.class) {
			rfd.type = ReflectionFieldType.REFERENCEKEY;
			String targetName = StringUtils.removeEnd(rfd.name, "Key");
			excludedFields.add(targetName);
			return;
		}

		if (rfd.clazz == int.class || rfd.clazz == Integer.class) {
			rfd.type = ReflectionFieldType.INT;
			return;
		}

		if (rfd.clazz == double.class || rfd.clazz == Double.class) {
			rfd.type = ReflectionFieldType.DOUBLE;
			return;
		}

		if (rfd.clazz == float.class || rfd.clazz == Float.class) {
			rfd.type = ReflectionFieldType.FLOAT;
			return;
		}

		if (rfd.clazz == long.class || rfd.clazz == Long.class) {
			rfd.type = ReflectionFieldType.LONG;
			return;
		}

		if (rfd.clazz == boolean.class || rfd.clazz == Boolean.class) {
			rfd.type = ReflectionFieldType.BOOLEAN;
			return;
		}
		if (rfd.clazz == String.class) {
			rfd.type = ReflectionFieldType.STRING;
			return;
		}
		if (rfd.clazz == Date.class) {
			rfd.type = ReflectionFieldType.DATE;
			return;
		}

		// this is a subobject because key fields will pick up references
		if (fieldImplements(rfd.clazz, DomainObject.class)) {
			rfd.type = ReflectionFieldType.SUBOBJECT;
			return;
		}

		if (fieldImplements(rfd.clazz, ServiceFactory.class)) {
			rfd.type = ReflectionFieldType.CONTROL;
			return;
		}

		if (fieldImplements(rfd.clazz, List.class)) {
			String fullGenericDeclaration = rfd.field.getGenericType()
					.toString();
			String[] s = StringUtils.split(fullGenericDeclaration, "<");
			String genericTypeName = StringUtils.chomp(s[1], ">");
			rfd.genericType = Class.forName(genericTypeName);
			if (rfd.genericType == Key.class) {
				rfd.type = ReflectionFieldType.REFERENCEKEYLIST;
				// also remove related list - we don't want a widget for that
				String targetName = StringUtils.removeEnd(rfd.name, "Keys");
				excludedFields.add(targetName);
				return;
			}
			if (fieldImplements(rfd.genericType, DomainObject.class)) {
				rfd.type = ReflectionFieldType.SUBLIST;
				return;
			}
			if (fieldImplements(rfd.genericType, PropertyChangeListener.class)) {
				rfd.type = ReflectionFieldType.CONTROL;
				return;
			}
			if (fieldImplements(rfd.genericType, StructureChangeListener.class)) {
				rfd.type = ReflectionFieldType.CONTROL;
				return;
			}
		}

		if (rfd.type == null) {
			logger.error("No reflection field type defined for type " + rfd.clazz.getName() + " for field " + rfd.name);
		}

		// if (fieldImplements(fieldType, List.class)) {
		// if (field.isAnnotationPresent(ReferenceList.class)) {
		// logger.info("ReferenceList annotation, generic type is " + field.getGenericType().toString());
		// return ReflectionFieldType.REFERENCELIST;
		// }
		// }
		//		
	}

	private void labelText(Map<String, ReflectionFieldDescriptor> fieldMap, Class<?> clazz) {
		for (ReflectionFieldDescriptor rfd : fieldMap.values()) {
			rfd.labelText = Q3StringUtil.splitAtCamelCase(rfd.name);
			switch (rfd.type) {
			case REFERENCEKEY:
				rfd.labelText = StringUtils.removeEndIgnoreCase(rfd.labelText, "key");
				break;
			case REFERENCEKEYLIST:
				rfd.labelText = StringUtils.removeEndIgnoreCase(rfd.labelText, "keys");
				break;
			}
			if (rfd.labelText.equals("key id")) {
				rfd.labelText = "id";
			}
			rfd.labelText = rfd.labelText.trim();
			logger.debug("Field " + rfd.name + " Label text set to " + rfd.labelText);
		}
	}

	private void createFieldMap(Map<String, Field> baseFieldMap) {
		fieldMap = new TreeMap<String, ReflectionFieldDescriptor>();
		for (Map.Entry<String, Field> entry : baseFieldMap.entrySet()) {
			ReflectionFieldDescriptor rfd = new ReflectionFieldDescriptor();
			rfd.name = entry.getKey();
			rfd.field = entry.getValue();
			rfd.clazz = rfd.field.getType();
			fieldMap.put(rfd.name, rfd);

		}
	}

	/**
	 * create a codegenMethod for setters for view, widget, and domain
	 * 
	 * @param fieldMap
	 * @param clazz
	 */
	private void gettersAndSetters(Map<String, ReflectionFieldDescriptor> fieldMap, Class<?> clazz) {
		logger.debug("Creating getters and setters");
		for (ReflectionFieldDescriptor rfd : fieldMap.values()) {
			try {

				// Domain
				// make sure one exists in the domain object itself
				// but not if a calculated field
				// calculated field should have no domain setter
				rfd.setter = "set" + StringUtils.capitalize(rfd.name);
				if (!rfd.isCalculated) {
					clazz.getMethod(rfd.setter, new Class<?>[] { rfd.field.getType() });
					rfd.domainSetValueMethod = (CodegenMethod) cgDomainObject.addMethod(rfd.setter)
							.addParameter(rfd.clazz, rfd.name);
					logger.debug("Domain setter set to " + rfd.domainSetValueMethod.getName());
				} else {
					logger.debug("Domain setter set to null as field is calculated");
				}

				// identifies the domain getter and places in rfd
				domainGetterName(rfd, clazz);

				// Widget
				String widgetsetter = Q3StringUtil.methodNamefromfieldName("set", rfd.widgetName + "DataValue");
				rfd.widgetSetDataValueMethod = (CodegenMethod) new CodegenMethod(null, widgetsetter).addParameter(rfd.clazz, rfd.name);
				logger.debug("Widget setter set to " + rfd.widgetSetDataValueMethod.getName());

				// View
				String viewSetter = Q3StringUtil.methodNamefromfieldName("set", rfd.widgetName + "DataValue");
				rfd.viewSetWidgetDataValueMethod = (CodegenMethod) cgViewClass.addMethod(viewSetter)
						.addParameter(rfd.clazz, rfd.name);
				rfd.viewSetWidgetDataValueMethod.interfaceMember(true);
				logger.debug("View setter set to " + rfd.viewSetWidgetDataValueMethod.getName());

				// Widget

				String widgetgetter = Q3StringUtil.methodNamefromfieldName("get", "DataValue");
				rfd.widgetGetDataValueMethod = (CodegenMethod) new CodegenMethod(null, widgetgetter).returns(rfd.clazz);
				logger.debug("Widget data value getter set to " + rfd.widgetGetDataValueMethod);
				// View
				String viewGetter = Q3StringUtil.methodNamefromfieldName("get", rfd.widgetName + "DataValue");
				rfd.viewGetWidgetDataValueMethod = (CodegenMethod) cgViewClass.addMethod(viewGetter)
						.returns(rfd.clazz);
				rfd.viewGetWidgetDataValueMethod.interfaceMember(true);

				rfd.viewGetWidgetDataValueMethod.addBodyLine("return " + rfd.widgetGetDataValueMethod.invocationCode(rfd.widgetName, null));
				logger.debug("View getter set to " + rfd.viewGetWidgetDataValueMethod.getName());

			} catch (Exception e) {
				logger.error(rfd.setter + " does not exist - shold this be a calculated field?", e);
				rfd.setter = null;
			}
			if (rfd.domainGetValueMethod == null) {
				logger.error("domainGetValueMethod has not been set for " + rfd.name);
			}
		}

	}

	/**
	 * populates the rfd.getter field and creates a codegenMethod for domain getter - looks for getNnnn, hasNnnn, isNnnn, nnnn() where nnnn
	 * is the fieldName
	 * 
	 * @param fieldMap
	 */
	private void domainGetterName(ReflectionFieldDescriptor rfd, Class<?> clazz) {
		rfd.getter = rfd.name;
		try {
			clazz.getMethod(rfd.getter, (Class<?>[]) null);
		} catch (Exception e) {
			rfd.getter = "get" + StringUtils.capitalize(rfd.name);
			try {
				clazz.getMethod(rfd.getter, (Class<?>[]) null);
			} catch (Exception e1) {
				rfd.getter = "has" + StringUtils.capitalize(rfd.name);
				try {
					clazz.getMethod(rfd.getter, (Class<?>[]) null);
				} catch (Exception e2) {
					rfd.getter = "is" + StringUtils.capitalize(rfd.name);
					try {
						clazz.getMethod(rfd.getter, (Class<?>[]) null);
					} catch (Exception e3) {
						logger.error("Unable to find getter for " + rfd.name + " field is ignored");
						rfd.getter = null;
						rfd.type = ReflectionFieldType.UNKNOWN;
					}
				}
			}

		}

		// Domain
		rfd.domainGetValueMethod = (CodegenMethod) cgDomainObject.addMethod(rfd.getter)
				.returns(rfd.clazz);
		logger.debug("Domain getter set to " + rfd.domainGetValueMethod);

	}

	/**
	 * Assigns the default widget type for the given field type
	 */
	private void assignWidgets() {
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {

			switch (fr.type) {

			case REFERENCE:
				fr.widgetDef = WidgetDef.REFERENCEWIDGET;
				break;
			case REFERENCELIST:
				fr.widgetDef = WidgetDef.REFERENCELISTWIDGET;
				break;
			case SUBLIST:
				fr.widgetDef = WidgetDef.SUBLISTWIDGET;
				break;
			default:
				fr.widgetDef = defaultWidgetForType(fr);
			}
			if (fr.widgetDef == null) {
				failures++;
				logger.error("Widget definition not specified for " + fr.name);
			} else {
				if (!fr.widgetDef.equals(WidgetDef.NULL)) {
				}
			}

		}
	}

	//
	// /**
	// * Fields which have been identified as {@link ReflectionFieldType#REFERENCE} or {@link ReflectionFieldType#REFERENCELIST} are not
	// * actually displayed but their partner object is (partner object for example is where assignedResourceKey is partnered with
	// * assignedResource). The partner needs therefore to be identified as a reference so that it will display a hyperlink or list of
	// * hyperlinks if it is a list of references - this cannot be done easily during the first pass because there is no way of knowing the
	// * order in which fields may be declared.
	// *
	// * @param fieldMap
	// */
	// private void resolveKeyReferences(Map<String, ReflectionFieldDescriptor> fieldMap) {
	// logger.debug("Resolving references");
	// for (ReflectionFieldDescriptor rfd : fieldMap.values()) {
	// switch (rfd.type) {
	// case REFERENCE:
	// logger.debug("Resolving reference for " + rfd.name);
	// // remove 'Key' from the name
	// String referred = rfd.name.substring(0, rfd.name.length() - 3);
	// fieldMap.get(referred).type = ReflectionFieldType.REFERENCE;
	// rfd.type = ReflectionFieldType.IGNORE;
	// break;
	// case REFERENCEKEYLIST:
	// // remove 'Keys' from the name
	// logger.debug("Resolving reference list for " + rfd.name);
	// referred = rfd.name.substring(0, rfd.name.length() - 4);
	// fieldMap.get(referred).type = ReflectionFieldType.REFERENCELIST;
	// rfd.type = ReflectionFieldType.IGNORE;
	//
	// break;
	// default:
	// // do nothing
	// }
	// }
	// }

	/**
	 * returns the default widget class for the given type
	 */
	private WidgetDef defaultWidgetForType(ReflectionFieldDescriptor rfd) {
		ReflectionFieldType type = rfd.type;

		switch (type) {
		case INT:
			return WidgetDef.TEXTBOXINTEGER;
		case DOUBLE:
			return WidgetDef.TEXTBOXDOUBLE;
		case STRING:
			return WidgetDef.TEXTBOXSTRING;
		case LONG:
			return WidgetDef.TEXTBOXLONG;
		case BOOLEAN:
			return WidgetDef.CHECKBOX;
		case DATE:
			return WidgetDef.DATEBOX;
		case COLLECTION:
		case DOMAIN:
			return WidgetDef.NULL;
		case REFERENCELIST:
			return WidgetDef.NULL;
		case REFERENCE:
			return WidgetDef.NULL;
		case REFERENCEKEY:
			return WidgetDef.REFERENCEWIDGET;
		case REFERENCEKEYLIST:
			return WidgetDef.REFERENCELISTWIDGET;
		default:
			logger.error("No widget defined for type " + type + " [field is " + rfd.name + "]");
			failures++;
			return WidgetDef.NULL;
		}

	}

	private void print() {
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {
			System.out.println(fr);
		}
	}

	private void printNames() {
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {
			System.out.println(fr.name);
		}
	}

	private boolean fieldImplements(Class<? extends Object> fieldType, Class<?> interfaceClass) {
		if (fieldType == interfaceClass) {
			return true;
		}
		Class<?> clazz = fieldType;
		while (clazz != null) {
			Class<?>[] intfs = clazz.getInterfaces();
			// ArrayList<Class<?>> list=new ArrayList<Class<?>>();
			for (int i = 0; i < intfs.length; i++) {
				if (intfs[i] == interfaceClass) {
					return true;
				}
			}
			clazz = clazz.getSuperclass();
		}
		return false;
	}

	private void fieldsInUI() throws ClassNotFoundException {
		for (ReflectionFieldDescriptor fr : fieldMap.values()) {

			if (fr.widgetDef == null) {
				logger.error("Widget def is null for " + fr.name);
			}
			if (fr.widgetDef.clazz() == null) {
				logger.error("Widget def class is null for " + fr.name);
			}

			// add widgets as fields, protected so visible to subclass, and create getters/setters
			cgViewClass.addField(fr.widgetName, fr.widgetDef.clazz())
					.visibility(Visibility.PROTECTED)
					.setter(true)
					.getter(true);

			// Many widgets have a separate label, but not all
			if (!fr.selfLabelling()) {
				cgViewClass.addField(fr.labelName, Label.class)
						.visibility(Visibility.PROTECTED);
			}
			/**
			 * String getterName = Q3StringUtil.methodNamefromfieldName("get", fr.widgetName + "Value"); CodegenMethodBase m =
			 * cgViewClass.addMethod(getterName) .returns(fr.widgetDef.valueType()) .visibility(Visibility.PUBLIC); CodegenMethod method =
			 * (CodegenMethod) m; method.interfaceMember(true); m.addBodyLine("return " + fr.widgetName + "." +
			 * fr.widgetDef.getValueMethod()); fr.viewGetValueMethod = (CodegenMethod) m;
			 * 
			 * String setterName = Q3StringUtil.methodNamefromfieldName("set", fr.widgetName + "Value"); CodegenMethod m1 =
			 * cgViewClass.addMethod(setterName); m1.visibility(Visibility.PUBLIC); m1.interfaceMember(true);
			 * m1.addParameter(fr.widgetDef.valueType(), fr.name); m1.addBodyLine(fr.widgetDef.callSetValueMethod(fr.widgetName, fr.name));
			 * fr.viewSetValueMethod = m1;
			 * 
			 * 
			 * // fieldsBuf.append(fieldDeclaration(fr.widgetType, fr.widgetName)); // fieldsBuf.append("\tprotected " + "Label " +
			 * fr.labelName + ";\n");
			 **/
		}

	}

	private void assignWidgetNames() {
		for (ReflectionFieldDescriptor rfd : fieldMap.values()) {
			String widgetNameBase = Q3StringUtil.lowerCaseFirstChar(rfd.name);
			rfd.widgetName = widgetNameBase + "Widget";
			rfd.labelName = widgetNameBase + "Label";
		}
	}

	// private Class<?> widgetvalueType(ReflectionFieldDescriptor fr) {
	// Class<? extends Widget> widgetClass = fr.widgetType;
	// if (widgetClass == TextBox.class) {
	// return String.class;
	// }
	// if (widgetClass == DateBox.class) {
	// return Date.class;
	// }
	// if (widgetClass == ReferenceWidget.class) {
	// if (fr.clazz == null) {
	// logger.error(fr.name + " clazz is null but is needed for ReferenceWidget");
	// }
	// // return fr.clazz;
	// return String.class;
	// }
	// if (widgetClass == ReferenceListWidget.class) {
	// return List.class;
	// }
	// if (widgetClass == CheckBox.class) {
	// return boolean.class;
	// }
	// if (widgetClass == ListBox.class) {
	// return List.class;
	// }
	//
	// logger.error(widgetClass.getName());
	// throw new RuntimeException("not yet implemented");
	// }

	// private String widgetGetterMethod(ReflectionFieldDescriptor fr) {
	// Class<? extends Widget> widgetClass = fr.widgetType;
	// if (widgetClass == TextBox.class) {
	// return "getText";
	// }
	// if (widgetClass == DateBox.class) {
	// return "getValue";
	// }
	// if (widgetClass == ReferenceListWidget.class) {
	// return "getValues";
	// }
	// return "getValue";
	// }
	//
	// private String widgetSetterMethod(ReflectionFieldDescriptor fr) {
	// Class<? extends Widget> widgetClass = fr.widgetType;
	// if (widgetClass == TextBox.class) {
	// return "setText";
	// }
	// if (widgetClass == DateBox.class) {
	// return "setValue";
	// }
	// if (widgetClass == ReferenceListWidget.class) {
	// return "setValues";
	// }
	// return "setValue";
	// }

}
